package com.hup.joystick.service.jna;

import com.hup.joystick.constant.DeviceKey.NativeKeys;
import com.sun.jna.NativeLong;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import lombok.Getter;

import java.util.Arrays;

/**
 * 设备按键状态,对应 dinput.h的DIJOYSTATE2;
 * 没深究这个类,猜想:
 * .此类的多余long字段是用来同时返回4个设备的状态(其他同类软件都是最大支持4个设备)
 * .rgdwPOV[4]和rgbButtons[128]也是4个设备共用的
 * .Slider目前设备没有任何返回值,所以完全不了解
 */
@SuppressWarnings("SpellCheckingInspection")
@Getter
@FieldOrder({"lX", "lY", "lZ", "lRx", "lRy", "lRz",
        "rglSlider", "rgdwPOV", "rgbButtons",
        "lVX", "lVY", "lVZ", "lVRx", "lVRy", "lVRz", "rglVSlider",
        "lAX", "lAY", "lAZ", "lARx", "lARy", "lARz", "rglASlider",
        "lFX", "lFY", "lFZ", "lFRx", "lFRy", "lFRz", "rglFSlider"})
public class DIJoyState2 extends Structure {
    public static final DIJoyState2 EMPTY_STATE = emptyState();

    /**
     * 摇杆轴
     * 使用NativeLong是因为cpp的long是32位的,java的是64,直接使用long会导致转换时数据错乱
     *
     */
    public NativeLong lX;
    public NativeLong lY;
    /**
     * LR2(XInput) or rightStick(DInput,HID)
     */
    public NativeLong lZ;
    public NativeLong lRx;
    public NativeLong lRy;
    /**
     * LR2(XInput) or rightStick(DInput,HID)
     */
    public NativeLong lRz;
    //未知字段
    public NativeLong[] rglSlider = new NativeLong[2];
    /**
     * 方向键
     * cpp中是long类型32位,java的int也是32位
     */
    public int[] rgdwPOV = new int[4];
    //按钮
    public byte[] rgbButtons = new byte[128];
    //下面都是未知字段
    public NativeLong lVX;        /* 'v' as in velocity */
    public NativeLong lVY;
    public NativeLong lVZ;
    public NativeLong lVRx;
    public NativeLong lVRy;
    public NativeLong lVRz;
    public NativeLong[] rglVSlider = new NativeLong[2];
    //
    public NativeLong lAX;        /* 'a' as in acceleration */
    public NativeLong lAY;
    public NativeLong lAZ;
    public NativeLong lARx;
    public NativeLong lARy;
    public NativeLong lARz;
    public NativeLong[] rglASlider = new NativeLong[2];
    //
    public NativeLong lFX;        /* 'f' as in force */
    public NativeLong lFY;
    public NativeLong lFZ;
    public NativeLong lFRx;        /* 'fr' as in rotational force aka torque */
    public NativeLong lFRy;
    public NativeLong lFRz;
    public NativeLong[] rglFSlider = new NativeLong[2];

    /**
     * 打印状态,仅含被按下按键的信息,仅含基础按键,不确定描述一定准确
     */
    @Override
    public String toString() {
        return getActiveString(true);
    }

    /**
     * 打印状态,仅含被按下按键的信息,不确定描述一定准确
     *
     * @param onlyNormal true-仅含基础按键
     */
    public String getActiveString(boolean onlyNormal) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (lX.longValue() != 0 || lY.longValue() != 0)
            sb.append("leftStick[").append(lX).append(',').append(lY).append("], ");
        if (lRx.longValue() != 0 || lRy.longValue() != 0)
            sb.append("rightStick[").append(lRx).append(',').append(lRy).append("], ");
        if (lZ.longValue() != 0 || lRz.longValue() != 0)
            sb.append("LR2(XInput) or rightStick(DInput,HID)[").append(lZ).append(',').append(lRz).append("], ");
        for (int i = 0; i < rgdwPOV.length; i++) {
            if (rgdwPOV[i] != NativeKeys.ARROW_DEF_VALUE)
                sb.append("arrow[").append(i).append("]=").append(rgdwPOV[i]).append(", ");
        }

        boolean hasBtn = false;
        for (int i = 0; i < rgbButtons.length; i++) {
            if ((rgbButtons[i] & 0x80) != 0) {
                if (!hasBtn) sb.append("button[");
                if (i < 10) sb.append(0);
                sb.append(i).append(' ');
                hasBtn = true;
            }
        }
        if (hasBtn) sb.append(']');

        if (!onlyNormal) {
            appendArray(sb, rglSlider, "Slider");
            appendLong(sb, lVX, "VX");
            appendLong(sb, lVY, "VY");
            appendLong(sb, lVZ, "VZ");
            appendLong(sb, lVRx, "VRx");
            appendLong(sb, lVRy, "VRy");
            appendLong(sb, lVRz, "VRz");
            appendArray(sb, rglVSlider, "VSlider");
            appendLong(sb, lAX, "AX");
            appendLong(sb, lAY, "AY");
            appendLong(sb, lAZ, "AZ");
            appendLong(sb, lARx, "ARx");
            appendLong(sb, lARy, "ARy");
            appendLong(sb, lARz, "ARz");
            appendArray(sb, rglASlider, "ASlider");
            appendLong(sb, lFX, "FX");
            appendLong(sb, lFY, "FY");
            appendLong(sb, lFZ, "FZ");
            appendLong(sb, lFRx, "FRx");
            appendLong(sb, lFRy, "FRy");
            appendLong(sb, lFRz, "FRz");
            appendArray(sb, rglFSlider, "FSlider");
        }

        sb.append(']');
        return sb.toString();
    }

    private void appendLong(StringBuilder sb, NativeLong l, String desc) {
        if (l.longValue() != 0)
            sb.append(desc).append('=').append(l.longValue()).append(", ");
    }

    private void appendArray(StringBuilder sb, NativeLong[] array, String desc) {
        if ((array[0] != null && array[0].longValue() != 0) || (array[1] != null && array[1].longValue() != 0))
            sb.append(desc).append('[').append(array[0].longValue()).append(',').append(array[1].longValue()).append("], ");
    }

    /**
     * 打印所有值
     */
    @SuppressWarnings("unused")
    public String getAllString() {
        return "DIJoyState2{" +
                "lX=" + lX +
                ", lY=" + lY +
                ", lZ=" + lZ +
                ", lRx=" + lRx +
                ", lRy=" + lRy +
                ", lRz=" + lRz +
                ", rglSlider=" + Arrays.toString(rglSlider) +
                ", rgdwPOV=" + Arrays.toString(rgdwPOV) +
                ", rgbButtons=" + Arrays.toString(rgbButtons) +
                ", lVX=" + lVX +
                ", lVY=" + lVY +
                ", lVZ=" + lVZ +
                ", lVRx=" + lVRx +
                ", lVRy=" + lVRy +
                ", lVRz=" + lVRz +
                ", rglVSlider=" + Arrays.toString(rglVSlider) +
                ", lAX=" + lAX +
                ", lAY=" + lAY +
                ", lAZ=" + lAZ +
                ", lARx=" + lARx +
                ", lARy=" + lARy +
                ", lARz=" + lARz +
                ", rglASlider=" + Arrays.toString(rglASlider) +
                ", lFX=" + lFX +
                ", lFY=" + lFY +
                ", lFZ=" + lFZ +
                ", lFRx=" + lFRx +
                ", lFRy=" + lFRy +
                ", lFRz=" + lFRz +
                ", rglFSlider=" + Arrays.toString(rglFSlider) +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DIJoyState2 s1 = this;
        DIJoyState2 s2 = (DIJoyState2) o;
        if (s1.lX.longValue() != s2.lX.longValue()
                || s1.lY.longValue() != s2.lY.longValue()
                || s1.lZ.longValue() != s2.lZ.longValue()
                || s1.lRx.longValue() != s2.lRx.longValue()
                || s1.lRy.longValue() != s2.lRy.longValue()
                || s1.lRz.longValue() != s2.lRz.longValue()
                //
                || s1.lVX.longValue() != s2.lVX.longValue()
                || s1.lVY.longValue() != s2.lVY.longValue()
                || s1.lVZ.longValue() != s2.lVZ.longValue()
                || s1.lVRx.longValue() != s2.lVRx.longValue()
                || s1.lVRy.longValue() != s2.lVRy.longValue()
                || s1.lVRz.longValue() != s2.lVRz.longValue()
                //
                || s1.lAX.longValue() != s2.lAX.longValue()
                || s1.lAY.longValue() != s2.lAY.longValue()
                || s1.lAZ.longValue() != s2.lAZ.longValue()
                || s1.lARx.longValue() != s2.lARx.longValue()
                || s1.lARy.longValue() != s2.lARy.longValue()
                || s1.lARz.longValue() != s2.lARz.longValue()
                //
                || s1.lFX.longValue() != s2.lFX.longValue()
                || s1.lFY.longValue() != s2.lFY.longValue()
                || s1.lFZ.longValue() != s2.lFZ.longValue()
                || s1.lFRx.longValue() != s2.lFRx.longValue()
                || s1.lFRy.longValue() != s2.lFRy.longValue()
                || s1.lFRz.longValue() != s2.lFRz.longValue()) {
            return false;
        }
        for (int i = 0; i < 2; i++) if (s1.rglSlider[i].longValue() != s2.rglSlider[i].longValue()) return false;
        for (int i = 0; i < 4; i++) if (s1.rgdwPOV[i] != s2.rgdwPOV[i]) return false;
        for (int i = 0; i < 128; i++) if (s1.rgbButtons[i] != s2.rgbButtons[i]) return false;
        for (int i = 0; i < 2; i++) if (s1.rglVSlider[i].longValue() != s2.rglVSlider[i].longValue()) return false;
        for (int i = 0; i < 2; i++) if (s1.rglASlider[i].longValue() != s2.rglASlider[i].longValue()) return false;
        for (int i = 0; i < 2; i++) if (s1.rglFSlider[i].longValue() != s2.rglFSlider[i].longValue()) return false;
        return true;
    }

    /**
     * @return 无按下按键的状态
     */
    public static DIJoyState2 emptyState() {
        DIJoyState2 s = new DIJoyState2();
        s.lX = new NativeLong();
        s.lY = new NativeLong();
        s.lZ = new NativeLong();
        s.lRx = new NativeLong();
        s.lRy = new NativeLong();
        s.lRz = new NativeLong();
        initLongArray(s.rglSlider);
        for (int i = s.rgdwPOV.length - 1; i >= 0; i--) s.rgdwPOV[i] = 0xFFFFFFFF;
        for (int i = s.rgbButtons.length - 1; i >= 0; i--) s.rgbButtons[i] = 0;
        //
        s.lVX = new NativeLong();
        s.lVY = new NativeLong();
        s.lVZ = new NativeLong();
        s.lVRx = new NativeLong();
        s.lVRy = new NativeLong();
        s.lVRz = new NativeLong();
        initLongArray(s.rglVSlider);
        //
        s.lAX = new NativeLong();
        s.lAY = new NativeLong();
        s.lAZ = new NativeLong();
        s.lARx = new NativeLong();
        s.lARy = new NativeLong();
        s.lARz = new NativeLong();
        initLongArray(s.rglASlider);
        //
        s.lFX = new NativeLong();
        s.lFY = new NativeLong();
        s.lFZ = new NativeLong();
        s.lFRx = new NativeLong();
        s.lFRy = new NativeLong();
        s.lFRz = new NativeLong();
        initLongArray(s.rglFSlider);
        return s;
    }

    private static void initLongArray(NativeLong[] array) {
        for (int i = array.length - 1; i >= 0; i--) array[i] = new NativeLong();

    }

}